package main

import (
    "fmt"
    "os"
    "os/exec"
    "path/filepath"
)

func main() {
    // 检查命令行参数
    args := os.Args[1:]
    if len(args) > 0 {
        switch args[0] {
        case "add-plugin":
            if len(args) < 2 {
                fmt.Println("Usage: app add-plugin <plugin-file>")
                os.Exit(1)
            }
            if err := addPlugin(args[1]); err != nil {
                fmt.Printf("Failed to add plugin: %v\n", err)
                os.Exit(1)
            }
            fmt.Printf("Plugin %s added successfully\n", args[1])
            return
        case "remove-plugin":
            if len(args) < 2 {
                fmt.Println("Usage: app remove-plugin <plugin-name>")
                os.Exit(1)
            }
            if err := removePlugin(args[1]); err != nil {
                fmt.Printf("Failed to remove plugin: %v\n", err)
                os.Exit(1)
            }
            fmt.Printf("Plugin %s removed successfully\n", args[1])
            return
        case "list-plugins":
            if err := listPlugins(); err != nil {
                fmt.Printf("Failed to list plugins: %v\n", err)
                os.Exit(1)
            }
            return
        }
    }

    fmt.Println("XJar Plus Application Launcher")

    // 获取当前可执行文件的目录
    exePath, _ := os.Executable()
    exeDir := filepath.Dir(exePath)

    // 检查app.x是否存在
    appPath := filepath.Join(exeDir, "app.x")
    if _, err := os.Stat(appPath); os.IsNotExist(err) {
        fmt.Printf("Error: app.x not found at %s\n", appPath)
        os.Exit(1)
    }

    // 提取插件
    pluginDir := filepath.Join(exeDir, "plugins")
    
    // 创建插件目录（如果不存在）
    if err := os.MkdirAll(pluginDir, os.ModePerm); err != nil {
        fmt.Printf("Warning: Failed to create plugins directory: %v\n", err)
    }

    // 直接将插件中的类文件合并到基座项目中
    if err := mergePluginsToBaseInPlace(appPath, pluginDir); err != nil {
        fmt.Printf("Warning: Failed to merge plugins: %v\n", err)
    } else {
        fmt.Println("Plugins merged successfully into base application")
    }

    // 运行Java命令启动应用
    // 使用java -jar方式启动Spring Boot应用
    cmd := exec.Command("java", "-jar", appPath)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    fmt.Println("Starting application...")
    if err := cmd.Run(); err != nil {
        fmt.Printf("Error running application: %v\n", err)
        os.Exit(1)
    }
}

// addPlugin 添加插件
func addPlugin(pluginFile string) error {
    // 获取可执行文件所在目录
    exeDir := filepath.Dir(os.Args[0])
    pluginsDir := filepath.Join(exeDir, "plugins")

    // 创建插件目录（如果不存在）
    if err := os.MkdirAll(pluginsDir, os.ModePerm); err != nil {
        return fmt.Errorf("failed to create plugins directory: %v", err)
    }

    // 检查插件文件是否存在
    if _, err := os.Stat(pluginFile); os.IsNotExist(err) {
        return fmt.Errorf("plugin file does not exist: %s", pluginFile)
    }

    // 获取插件文件名
    pluginName := filepath.Base(pluginFile)

    // 检查是否为.x文件
    if filepath.Ext(pluginName) != ".x" {
        return fmt.Errorf("plugin file must have .x extension")
    }

    // 复制插件文件到插件目录
    pluginPath := filepath.Join(pluginsDir, pluginName)

    // 读取插件文件
    data, err := os.ReadFile(pluginFile)
    if err != nil {
        return fmt.Errorf("failed to read plugin file: %v", err)
    }

    // 写入插件文件
    if err := os.WriteFile(pluginPath, data, os.ModePerm); err != nil {
        return fmt.Errorf("failed to write plugin file: %v", err)
    }

    return nil
}

// removePlugin 移除插件
func removePlugin(pluginName string) error {
    // 获取可执行文件所在目录
    exeDir := filepath.Dir(os.Args[0])
    pluginsDir := filepath.Join(exeDir, "plugins")

    // 构建插件文件路径
    pluginPath := filepath.Join(pluginsDir, pluginName)

    // 检查插件文件是否存在
    if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
        return fmt.Errorf("plugin does not exist: %s", pluginName)
    }

    // 删除插件文件
    if err := os.Remove(pluginPath); err != nil {
        return fmt.Errorf("failed to remove plugin: %v", err)
    }

    return nil
}

// listPlugins 列出所有插件
func listPlugins() error {
    // 获取可执行文件所在目录
    exeDir := filepath.Dir(os.Args[0])
    pluginsDir := filepath.Join(exeDir, "plugins")

    // 检查插件目录是否存在
    if _, err := os.Stat(pluginsDir); os.IsNotExist(err) {
        fmt.Println("No plugins directory found")
        return nil
    }

    // 读取插件目录
    entries, err := os.ReadDir(pluginsDir)
    if err != nil {
        return fmt.Errorf("failed to read plugins directory: %v", err)
    }

    fmt.Println("Installed plugins:")
    for _, entry := range entries {
        if entry.IsDir() {
            continue
        }

        // 只显示.x文件
        if filepath.Ext(entry.Name()) == ".x" {
            info, _ := entry.Info()
            fmt.Printf("- %s (size: %d bytes)\n", entry.Name(), info.Size())
        }
    }

    return nil
}


// mergePluginsToBaseInPlace 直接将插件中的类文件合并到基座项目中，不创建新的JAR包
func mergePluginsToBaseInPlace(baseAppPath, pluginDir string) error {
    fmt.Printf("Merging plugins from %s into base application %s in-place\n", pluginDir, baseAppPath)
    fmt.Printf("Merging plugins from %s into base application %s with classes in BOOT-INF/classes\n", pluginDir, baseAppPath)

    // 检查基座应用是否存在
    if _, err := os.Stat(baseAppPath); os.IsNotExist(err) {
        return fmt.Errorf("base application not found: %s", baseAppPath)
    }

    // 检查插件目录是否存在
    if _, err := os.Stat(pluginDir); os.IsNotExist(err) {
        fmt.Println("No plugin directory found, skipping merge")
        return nil
    }

    // 创建临时目录用于解压基座应用和插件
    tempDir, err := os.MkdirTemp("", "app-merge")
    if err != nil {
        return fmt.Errorf("failed to create temp directory: %v", err)
    }
    defer os.RemoveAll(tempDir)

    baseDir := filepath.Join(tempDir, "base")
    mergedDir := filepath.Join(tempDir, "merged")

    // 解压基座应用
    fmt.Println("Extracting base application...")
    if err := unzip(baseAppPath, baseDir); err != nil {
        return fmt.Errorf("failed to unzip base application: %v", err)
    }

    // 验证解压后的内容
    if exists, _ := pathExists(baseDir); !exists {
        return fmt.Errorf("base directory not found after extraction: %s", baseDir)
    }

    // 复制基座应用内容到合并目录
    if err := copyDir(baseDir, mergedDir); err != nil {
        return fmt.Errorf("failed to copy base application to merged directory: %v", err)
    }

    // 确保BOOT-INF/classes目录存在
    classesDir := filepath.Join(mergedDir, "BOOT-INF", "classes")
    if err := os.MkdirAll(classesDir, os.ModePerm); err != nil {
        return fmt.Errorf("failed to create classes directory: %v", err)
    }

    // 读取插件目录
    entries, err := os.ReadDir(pluginDir)
    if err != nil {
        return fmt.Errorf("failed to read plugin directory: %v", err)
    }

    // 检查是否有插件
    pluginCount := 0
    for _, entry := range entries {
        if !entry.IsDir() && filepath.Ext(entry.Name()) == ".x" {
            pluginCount++
        }
    }

    if pluginCount == 0 {
        fmt.Println("No plugins found, skipping merge")
        return nil
    }

    fmt.Printf("Found %d plugin(s) to merge\n", pluginCount)

    // 处理每个插件
    for _, entry := range entries {
        if !entry.IsDir() && filepath.Ext(entry.Name()) == ".x" {
            pluginPath := filepath.Join(pluginDir, entry.Name())
            fmt.Printf("Processing plugin: %s\n", entry.Name())

            // 验证插件文件存在且可读
            if err := validatePlugin(pluginPath); err != nil {
                fmt.Printf("Warning: failed to validate plugin %s: %v\n", entry.Name(), err)
                continue
            }

            // 将插件内容合并到基座应用中
            if err := mergePluginClassesInPlace(pluginPath, baseDir); err != nil {
                fmt.Printf("Warning: failed to merge plugin %s: %v\n", entry.Name(), err)
                continue
            }

            fmt.Printf("Plugin %s merged successfully\n", entry.Name())
        }
    }

    // 将修改后的基座应用重新打包成JAR文件，直接覆盖原文件
    if err := createJarFromDirectory(baseDir, baseAppPath); err != nil {
        return fmt.Errorf("failed to create merged jar: %v", err)
    }

    fmt.Println("Plugin merge completed with classes in BOOT-INF/classes")
    return nil
}

// validatePlugin 验证插件文件的基本结构
func validatePlugin(pluginPath string) error {
    // 检查文件是否存在
    if exists, _ := pathExists(pluginPath); !exists {
        return fmt.Errorf("plugin file does not exist: %s", pluginPath)
    }

    // 创建临时目录用于快速验证
    tempDir, err := os.MkdirTemp("", "plugin-validate")
    if err != nil {
        return fmt.Errorf("failed to create temp directory for validation: %v", err)
    }
    defer os.RemoveAll(tempDir)

    // 尝试解压插件以验证其结构
    if err := unzip(pluginPath, tempDir); err != nil {
        return fmt.Errorf("invalid plugin format: %v", err)
    }

    return nil
}

// pathExists 检查路径是否存在
func pathExists(path string) (bool, error) {
    if _, err := os.Stat(path); os.IsNotExist(err) {
        return false, nil
    } else if err != nil {
        return false, err
    }
    return true, nil
}

// mergePluginClassesInPlace 将插件中的类文件合并到基座应用中
func mergePluginClassesInPlace(pluginPath, baseDir string) error {
    // 创建临时目录用于解压插件
    tempDir, err := os.MkdirTemp("", "plugin-extract")
    if err != nil {
        return fmt.Errorf("failed to create temp directory: %v", err)
    }
    defer os.RemoveAll(tempDir)

    // 解压插件到临时目录
    if err := unzip(pluginPath, tempDir); err != nil {
        return fmt.Errorf("failed to unzip plugin: %v", err)
    }

    // 获取基座应用的classes目录
    baseClassesDir := filepath.Join(baseDir, "BOOT-INF", "classes")

    // 遍历插件目录，将类文件复制到基座应用的classes目录
    return filepath.Walk(tempDir, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        // 跳过目录本身
        if path == tempDir {
            return nil
        }

        // 计算相对于临时目录的路径
        relPath, err := filepath.Rel(tempDir, path)
        if err != nil {
            return err
        }

        // 确定目标路径
        targetPath := filepath.Join(baseClassesDir, relPath)

        // 跳过META-INF目录及其子目录，以确保MANIFEST.MF文件不会被修改或覆盖
        if relPath == "META-INF" || filepath.HasPrefix(relPath, "META-INF/") {
            // 显式跳过MANIFEST.MF文件，避免任何可能的冲突或覆盖
            if relPath == "META-INF/MANIFEST.MF" || filepath.HasPrefix(relPath, "META-INF/MANIFEST.MF") {
                return nil
            }
            return nil
        }

        if info.IsDir() {
            // 创建目录
            return os.MkdirAll(targetPath, os.ModePerm)
        } else {
            // 复制文件
            return copyFile(path, targetPath)
        }
    })
}

// createJarFromDirectory 从目录创建JAR文件
func createJarFromDirectory(srcDir, outputPath string) error {
    // 确保MANIFEST.MF目录存在
    manifestDir := filepath.Join(srcDir, "META-INF")
    if err := os.MkdirAll(manifestDir, os.ModePerm); err != nil {
        return fmt.Errorf("failed to create META-INF directory: %v", err)
    }
    
    // 检查MANIFEST.MF是否存在
    manifestPath := filepath.Join(manifestDir, "MANIFEST.MF")
//     if _, err := os.Stat(manifestPath);  {
        // 如果MANIFEST.MF不存在，创建一个基本的
        // 对于Spring Boot可执行JAR文件，应该使用JarLauncher作为主类
        manifestContent := `Manifest-Version: 1.0
                            Created-By: Maven JAR Plugin 3.2.2
                            Build-Jdk-Spec: 21
                            Implementation-Title: base-project
                            Implementation-Version: 0.0.1-SNAPSHOT
                            Main-Class: org.springframework.boot.loader.JarLauncher
                            Start-Class: com.example.BaseProjectApplication
                            Spring-Boot-Version: 2.7.18
                            Spring-Boot-Classes: BOOT-INF/classes/
                            Spring-Boot-Lib: BOOT-INF/lib/
                            Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
                            Spring-Boot-Layers-Index: BOOT-INF/layers.idx



`
        if err := os.WriteFile(manifestPath, []byte(manifestContent), os.ModePerm); err != nil {
            return fmt.Errorf("failed to create manifest file: %v", err)
        }
//     }
    
    // 使用jar命令创建JAR文件，确保使用0压缩选项以避免嵌套JAR压缩问题
    // 同时确保保留MANIFEST.MF文件
    cmd := exec.Command("jar", "cf0", outputPath, ".")
    cmd.Dir = srcDir
    output, err := cmd.CombinedOutput()
    if err != nil {
        return fmt.Errorf("failed to create jar: %v, output: %s", err, string(output))
    }
    return nil
}


// unzip 解压ZIP文件到指定目录
func unzip(src, dest string) error {
    cmd := exec.Command("unzip", "-q", src, "-d", dest)
    output, err := cmd.CombinedOutput()
    if err != nil {
        return fmt.Errorf("failed to unzip: %v, output: %s", err, string(output))
    }
    return nil
}

// copyDir 复制目录
func copyDir(src, dst string) error {
    entries, err := os.ReadDir(src)
    if err != nil {
        return err
    }

    if err := os.MkdirAll(dst, os.ModePerm); err != nil {
        return err
    }

    for _, entry := range entries {
        srcPath := filepath.Join(src, entry.Name())
        dstPath := filepath.Join(dst, entry.Name())

        if entry.IsDir() {
            if err := copyDir(srcPath, dstPath); err != nil {
                return err
            }
        } else {
            if err := copyFile(srcPath, dstPath); err != nil {
                return err
            }
        }
    }

    return nil
}

// copyFile 复制文件
func copyFile(src, dst string) error {
    data, err := os.ReadFile(src)
    if err != nil {
        return err
    }

    // 确保目标目录存在
    dstDir := filepath.Dir(dst)
    if err := os.MkdirAll(dstDir, os.ModePerm); err != nil {
        return err
    }

    return os.WriteFile(dst, data, os.ModePerm)
}